/********************************************************************************
 * @file    bsp_uart.c
 * @author  jianqiang.xue
 * @version V1.0.0
 * @date    2021-04-13
 * @brief   uart驱动
 ********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "cx32l003.h"
#include "cx32l003_hal_uart.h"
#include "cx32l003_hal_rcc.h"
#include "cx32l003_hal_cortex.h"
#include "cx32l003_hal_def.h"

#include "bsp_exti.h"
#include "bsp_gpio.h"
#include "bsp_uart.h"

#include "ls_gpio.h"
#include "ls_syscfg.h"

#ifndef LS_APP_IO_CHANGE_SUPPORT
#define IO_TYPE(io)  (g_io_cfg[io].type)
#else // 支持可变
#include "app_io.h"
#define IO_TYPE(io)  (g_io_cfg[io].type)
#endif

/* Private Includes ----------------------------------------------------------*/
/* Private Define ------------------------------------------------------------*/
/* External Variables --------------------------------------------------------*/
/* Private Typedef -----------------------------------------------------------*/
typedef void (*bsp_uart_callback)(uint8_t data);

/* Private Variables ---------------------------------------------------------*/
#if LS_UART0_EN
#define BSP_UART0_APBX_CLOCK     RCC_PCLKEN_UART0CKEN
#define BSP_UART0_IRQN           UART0_IRQn
#define BSP_UART0_IRQ_HANDLER    UART0_IRQHandler

// 定义串口缓存区
static uint8_t *bsp_uart0_rx_buff = NULL;
static UART_HandleTypeDef *huart0 = NULL;
static bsp_uart_callback uart0_irq_rx_callback = NULL;
#endif

#if LS_UART1_EN
#define BSP_UART1_APBX_CLOCK     RCC_PCLKEN_UART1CKEN
#define BSP_UART1_IRQN           UART1_IRQn
#define BSP_UART1_IRQ_HANDLER    UART1_IRQHandler

// 定义串口缓存区
static uint8_t *bsp_uart1_rx_buff = NULL;
static UART_HandleTypeDef *huart1 = NULL;
static bsp_uart_callback uart1_irq_rx_callback = NULL;
#endif
/* Public Function Prototypes ------------------------------------------------*/
/**
 * @brief  初始化uart引脚
 * @param  io: 引脚号
 * @param  arf: 复用值
 */
static inline void io_cfg_as_uart(uint8_t io, uint8_t arf) {
    if (io == 0 || (io > LS_IO_NUM - 1)) return;
    GPIO_InitTypeDef GPIO_InitStruct = {
        .Pin             = g_io_cfg[io].io.pin,
        .Mode            = GPIO_MODE_AF,           // 【GPIO端口复用功能】
        .OpenDrain       = GPIO_PUSHPULL,          // 推免输出
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,  // 禁止输入去抖动
        .SlewRate        = GPIO_SLEW_RATE_HIGH,    // 电压转换速率
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH, // 驱动强度
        .Pull            = GPIO_PULLUP,            // 上拉
        .Alternate       = arf
    };
    SET_BIT(RCC->HCLKEN, g_io_cfg[io].io.periph);
    HAL_GPIO_Init(g_io_cfg[io].io.port, (GPIO_InitTypeDef*)&GPIO_InitStruct);
}

/**
 * @brief  设置串口波特率
 * @param  uart: 串口组号
 * @param  baud: 波特率
 * @retval 初始化结果  0--成功  >0失败
 */
uint8_t bsp_uart_set_baud_rate(bsp_uart_t uart, uint32_t baud) {
    if (uart == BSP_UART_0) {
#if LS_UART0_EN
        if (!huart0) return 1;
        __HAL_LOCK(huart0);
        HAL_NVIC_DisableIRQ(BSP_UART0_IRQN);
        huart0->ErrorCode     = HAL_UART_ERROR_NONE;
        huart0->gState        = HAL_UART_STATE_BUSY;
        huart0->Init.BaudRate = baud;
        /* Configure USART0 */
        HAL_UART_Init(huart0);
        huart0->gState = HAL_UART_STATE_READY;
        HAL_NVIC_EnableIRQ(BSP_UART0_IRQN);
        __HAL_UNLOCK(huart0);
        return 0;
#endif
    } else if (uart == BSP_UART_1) {
#if LS_UART1_EN
        if (!huart1) return 1;
        __HAL_LOCK(huart1);
        HAL_NVIC_DisableIRQ(BSP_UART1_IRQN);
        huart1->ErrorCode     = HAL_UART_ERROR_NONE;
        huart1->gState        = HAL_UART_STATE_BUSY;
        huart1->Init.BaudRate = baud;
        /* Configure USART1 */
        HAL_UART_Init(huart1);
        huart1->gState = HAL_UART_STATE_READY;
        HAL_NVIC_EnableIRQ(BSP_UART1_IRQN);
        __HAL_UNLOCK(huart1);
        return 0;
#endif
    }
    return 0xff;
}


#if LS_UART0_EN
/**
  * @brief  [uart接收数据回调函数] 用于不停接收数据，溢出后发送信号，停止接收
  */
__weak void uart0_rx_callback(uint8_t data) {
}
#endif

/**
 * @brief  串口初始化
 * @param  uart: 串口组号
 * @param  baud: 波特率
 * @param  tx_pin: tx引脚号
 * @param  rx_pin: rx引脚号
 * @retval 初始化结果  0--成功  >0失败
 */
uint8_t bsp_uart_init(bsp_uart_t uart, uint32_t baud, uint8_t tx_pin, uint8_t rx_pin) {
    if (uart == BSP_UART_0) {
#if LS_UART0_EN
        if (huart0) return 0; // 已经初始化过
        if (tx_pin == 0 || (tx_pin > LS_IO_NUM - 1) || rx_pin == 0 || (rx_pin > LS_IO_NUM - 1)) {
            bsp_uart_deinit(BSP_UART_0);
            return 1;
        }

        // 由于CX32L003的引脚复用值不一样，所以这里要区分
        if ((g_io_cfg[tx_pin].io.port == GPIOD) && (g_io_cfg[tx_pin].io.pin == (1 << 5))) {
            io_cfg_as_uart(tx_pin, GPIO_AF7_UART0_TXD);
        } else {
            io_cfg_as_uart(tx_pin, GPIO_AF5_UART0_TXD);
        }

        if ((g_io_cfg[rx_pin].io.port == GPIOD) && (g_io_cfg[rx_pin].io.pin == (1 << 6))) {
            io_cfg_as_uart(rx_pin, GPIO_AF7_UART0_RXD);
        } else {
            io_cfg_as_uart(rx_pin, GPIO_AF5_UART0_RXD);
        }

        bsp_uart0_rx_buff = malloc(LS_UART0_CACHE_SIZE); // 申请空间，并清零
        if (!bsp_uart0_rx_buff)  return 1; // 缓存无法申请这么大的空间

        huart0 = malloc(sizeof(UART_HandleTypeDef)); // 申请空间，并清零
        if (!huart0)  return 2; // 串口结构体无法申请空间
        // 结构体赋值
        huart0->Instance        = UART0;
        huart0->Init.BaudRate   = baud,
        huart0->Init.BaudDouble = UART_BAUDDOUBLE_ENABLE;
        huart0->Init.WordLength = UART_WORDLENGTH_8B;
        huart0->Init.Parity     = UART_PARITY_NONE;
        huart0->Init.Mode       = UART_MODE_TX_RX;
        huart0->pRxBuffPtr      = bsp_uart0_rx_buff;
        huart0->RxXferSize      = LS_UART0_CACHE_SIZE;
        bsp_exti_set(BSP_UART0_IRQN, 0);
        __HAL_RCC_UART0_CLK_ENABLE();
        bsp_uart_rx_irq_callback(BSP_UART_0, uart0_rx_callback);
        /* Configure USART0 */
        HAL_UART_Init(huart0);
        HAL_UART_Receive_IT(huart0, bsp_uart0_rx_buff, LS_UART0_CACHE_SIZE);
        return 0;
#endif
    } else if (uart == BSP_UART_1) {
#if LS_UART1_EN
        if (huart1) return 0; // 已经初始化过
        if (tx_pin == 0 || (tx_pin > LS_IO_NUM - 1) || rx_pin == 0 || (rx_pin > LS_IO_NUM - 1)) {
            bsp_uart_deinit(BSP_UART_1);
            return 1;
        }
        io_cfg_as_uart(io, GPIO_AF5_UART1_TXD);
        io_cfg_as_uart(io, GPIO_AF5_UART1_RXD);

        bsp_uart1_rx_buff = malloc(LS_UART1_CACHE_SIZE); // 申请空间，并清零
        if (!bsp_uart1_rx_buff)  return 1; // 缓存无法申请这么大的空间

        huart1 = malloc(sizeof(UART_HandleTypeDef)); // 申请空间，并清零
        if (!huart1)  return 2; // 串口结构体无法申请空间
        // 结构体赋值
        huart1->Instance        = UART1;
        huart1->Init.BaudRate   = baud,
        huart1->Init.BaudDouble = UART_BAUDDOUBLE_ENABLE;
        huart1->Init.WordLength = UART_WORDLENGTH_8B;
        huart1->Init.Parity     = UART_PARITY_NONE;
        huart1->Init.Mode       = UART_MODE_TX_RX;
        huart1->pRxBuffPtr      = bsp_uart1_rx_buff;
        huart1->RxXferSize      = LS_UART1_CACHE_SIZE;
        bsp_exti_set(BSP_UART1_IRQN, 0);
        __HAL_RCC_UART1_CLK_ENABLE();
        bsp_uart_rx_irq_callback(BSP_UART_1, uart1_rx_callback);
        /* Configure USART1 */
        HAL_UART_Init(huart1);
        HAL_UART_Receive_IT(huart1, bsp_uart1_rx_buff, LS_UART1_CACHE_SIZE);
        return 0;
#endif
    }
    return 0xff;
}

/**
 * @brief  串口反注册 关闭串口时钟并复位引脚
 * @param  uart: 串口组号
 */
void bsp_uart_deinit(bsp_uart_t uart) {
    if (uart == BSP_UART_0) {
#if LS_UART0_EN
        if (!huart0) return;
        HAL_UART_DeInit(huart0);

        free(huart0);
        huart0 = NULL;

        free(bsp_uart0_rx_buff);
        bsp_uart0_rx_buff = NULL;
        uart0_irq_rx_callback = NULL;
        __HAL_RCC_UART0_CLK_DISABLE();
#endif
    } else if (uart == BSP_UART_1) {
#if LS_UART1_EN
        if (!huart1) return;
        HAL_UART_DeInit(huart1);

        free(huart1);
        huart1 = NULL;

        free(bsp_uart1_rx_buff);
        bsp_uart1_rx_buff = NULL;
        uart1_irq_rx_callback = NULL;
        __HAL_RCC_UART1_CLK_DISABLE();
#endif
    }
}

/**
 * @brief  注册串口接收回调函数
 * @param  uart: 串口组号
 * @param  event: 事件回调函数
 * @retval 0--失败 1--成功
 */
bool bsp_uart_rx_irq_callback(bsp_uart_t uart, void* event) {
    if (uart == BSP_UART_0) {
        if (uart0_irq_rx_callback != NULL)
            return true;
        else
            uart0_irq_rx_callback = (bsp_uart_callback)event;
    }
    return false;
}

/**
 * @brief This function handles UART0 Interrupt .
 */
void BSP_UART0_IRQ_HANDLER(void) {
    /* USER CODE BEGIN UART0_IRQn 0 */
    if ((huart0->Instance->INTSR & UART_FLAG_RXNE) == UART_FLAG_RXNE) {
        HAL_UART_IRQHandler(huart0);
        if (uart0_irq_rx_callback)
            uart0_irq_rx_callback(bsp_uart0_rx_buff[huart0->RxXferSize - huart0->RxXferCount - 1]);
    }
    /* USER CODE END UART0_IRQn 0 */
}

#if LS_UART1_EN
/**
 * @brief This function handles UART1 Interrupt .
 */
void BSP_UART1_IRQ_HANDLER(void) {
    /* USER CODE BEGIN UART1_IRQn 0 */
    /* USER CODE END UART1_IRQn 0 */
}
#endif

/************************************[uart] 使用函数************************************/
/**
 * @brief  发送一个字节
 * @param  uart: 串口组号
 * @param  data: 字节值
 */
uint8_t bsp_uart_send_byte(bsp_uart_t uart, uint8_t data) {
    return bsp_uart_send_nbyte(uart, &data, 1);
}

/**
 * @brief  发送多个字节(堵塞)
 * @note   由于cx32不支持发送完成寄存器，所以这里采用中断发送完成，曲线实现功能。
 *         注：由于发送完成中断会导致接收中断受影响，必要关闭串口中断。
 * @param  uart: 串口组号
 * @param  *data: 数据头指针
 * @param  len: 数据长度
 */
uint8_t bsp_uart_send_nbyte(bsp_uart_t uart, uint8_t* data, uint16_t len) {
    if (uart == BSP_UART_0) {
        if (!huart0 || data == NULL) return false;
        /* Process Locked */
        __HAL_LOCK(huart0);
        HAL_NVIC_DisableIRQ(BSP_UART0_IRQN);
        /* 必须启用TC中断,以打开TC标志进行轮询 */
        __HAL_UART_ENABLE_IT(huart0, UART_IT_TC);
        huart0->ErrorCode = HAL_UART_ERROR_NONE;
        huart0->gState = HAL_UART_STATE_BUSY_TX;
        uint16_t i = 0;
        while (i < len) {
            UART0->SBUF = *(data + i);
            while ((huart0->Instance->INTSR & UART_FLAG_TC) != UART_FLAG_TC);
            huart0->Instance->INTCLR |= UART_FLAG_TC;
            i++;
        }
        huart0->gState = HAL_UART_STATE_READY;
        /* Process Unlocked */
        __HAL_UNLOCK(huart0);
        __HAL_UART_DISABLE_IT(huart0, UART_IT_TC);
        HAL_NVIC_EnableIRQ(BSP_UART0_IRQN);
    }
    return true;
}

/**
 * @brief  发送多个字节(非堵塞) 一般DMA方式
 * @param  uart: 串口组号
 * @param  *data: 数据头指针
 * @param  len: 数据长度
 */
void bsp_uart_send_nbyte_nowait(bsp_uart_t uart, uint8_t* data, uint16_t len) {
    if (data == NULL)
        return;
    if (uart == BSP_UART_0) {
        if (huart0)
            HAL_UART_Transmit_IT(huart0, data, len);
    }
    return;
}

/**
 * @brief  得到rxbuff头指针
 * @param  uart: 串口组号
 */
uint8_t* bsp_uart_get_rxbuff(bsp_uart_t uart) {
    if (uart == BSP_UART_0)
        return bsp_uart0_rx_buff;
    return NULL;
}

/**
 * @brief  [串口信息] 返回串口缓存指针位置，即当前缓存数量（byte）
 * @param  uart: 串口号
 * @retval 串口缓存指针位置
 */
uint16_t bsp_uart_get_rxbuff_position(bsp_uart_t uart) {
    if (uart == BSP_UART_0) {
        if (!huart0) return NULL;
        return huart0->RxXferSize - huart0->RxXferCount;
    }
    return NULL;
}

/**
 * @brief  得到rxbuff大小
 * @param  uart: 串口组号
 * @retval 字节大小
 */
uint16_t bsp_uart_get_rxbuff_size(bsp_uart_t uart) {
    if (uart == BSP_UART_0) {
        if (!huart0) return 0;
        return huart0->RxXferSize;
    }
    return 0;
}

/**
 * @brief  [串口操作] 关闭串口接收：关闭串口中断，终止接收中断回调
 * @note   由于无DMA，只能先关闭，再处理数据，防止数据错乱。然后重新读取。
 * @param  uart: 串口号
 */
void bsp_uart_rx_close(bsp_uart_t uart) {
    if (uart == BSP_UART_0) {
        if (!huart0) return;
        HAL_UART_AbortReceive(huart0);
    }
    return;
}

/**
 * @brief  [串口操作] 打开串口接收：打开串口中断，设置接收中断回调
 * @note   由于无DMA，只能先关闭，再处理数据，防止数据错乱。然后重新读取。
 * @param  uart: 串口号
 */
void bsp_uart_rx_open(bsp_uart_t uart) {
    if (uart == BSP_UART_0) {
        if (!huart0) return;
        HAL_UART_Receive_IT(huart0, bsp_uart0_rx_buff, huart0->RxXferSize);
    }
    return;
}

/**
 * @brief  [串口操作] 复位接收缓存指针
 * @param  uart: 串口号
 */
void bsp_uart_reset_rxbuff(bsp_uart_t uart) {
    if (uart == BSP_UART_0) {
        if (!huart0) return;
        HAL_NVIC_DisableIRQ(BSP_UART0_IRQN);
        huart0->pRxBuffPtr  = bsp_uart0_rx_buff;
        huart0->RxXferCount = huart0->RxXferSize;
        memset(bsp_uart0_rx_buff, 0, huart0->RxXferSize);
        HAL_UART_Receive_IT(huart0, bsp_uart0_rx_buff, huart0->RxXferSize);
        HAL_NVIC_EnableIRQ(BSP_UART0_IRQN);
    }
    return;
}

// 重定向printf
int fputc(int ch, FILE *f) {
    return bsp_uart_send_byte(BSP_UART_0, ch);
}
